from Utils.colors import *
from Utils.color_utils import *
import numpy as np
import math

"""
Road类：直接对国省道进行识别 读取的HSV的值为白色区域
"""


class Road:
    def __init__(self):
        self.road_color_lower = [0, 43, 46]
        self.road_color_upper = [10, 255, 255]

    def get_color_lower_upper(self, color: ColorRange()):
        road_color = color.get_road_color()   # [(),(),()]
        self.road_color_lower, self.road_color_upper = Utils.get_colors_lower_upper(road_color)

    def read_road_contours(self, color: ColorRange(), image_hsv):
        self.get_color_lower_upper(color)
        self.road_color_lower, self.road_color_upper = Utils.list_transform_array(self.road_color_lower, self.road_color_upper)
        mask = cv2.inRange(image_hsv, self.road_color_lower, self.road_color_upper)

        # 中值滤波
        result = cv2.medianBlur(mask, 3)

        # 进行二值化
        ret, thread = cv2.threshold(result, 0, 255, cv2.THRESH_BINARY)
        contours, hierarchy = cv2.findContours(thread, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)  # 坐标, 关系
        return contours

    def read_road_thread(self, color: ColorRange(), image_hsv):
        self.get_color_lower_upper(color)
        self.road_color_lower, self.road_color_upper = Utils.list_transform_array(self.road_color_lower, self.road_color_upper)
        mask = cv2.inRange(image_hsv, self.road_color_lower, self.road_color_upper)

        # 中值滤波
        result = cv2.medianBlur(mask, 3)

        # 进行二值化
        ret, thread = cv2.threshold(result, 0, 255, cv2.THRESH_BINARY)
        # contours, hierarchy = cv2.findContours(thread, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 坐标, 关系
        # return contours
        return thread


"""
RoadEdge类：主要完成对国省道的边沿和铁路的提取。HSV范围为灰色
"""


class RoadEdge:
    def __init__(self):
        self.road_color_lower = [0, 43, 46]
        self.road_color_upper = [10, 255, 255]

    def get_color_lower_upper(self, color: ColorRange()):
        road_color = color.get_road_color()   # [(),(),()]
        self.road_color_lower, self.road_color_upper = Utils.get_colors_lower_upper(road_color)

    def read_road_edge_contours(self, color: ColorRange(), image_hsv):
        """
        读取边沿
        :param color: HSV范围值
        :param image_hsv: 转换为hsv格式的图像
        :return:返回边沿点
        """
        self.get_color_lower_upper(color)
        self.road_color_lower, self.road_color_upper = Utils.list_transform_array(self.road_color_lower, self.road_color_upper)
        mask = cv2.inRange(image_hsv, self.road_color_lower, self.road_color_upper)

        # 中值滤波
        # result = cv2.medianBlur(mask, 3)

        # 进行二值化
        ret, thread = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)
        contours, hierarchy = cv2.findContours(thread, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 坐标, 关系
        return contours


"""
DiffVillageOrRoad类：直接对边缘点进行提取，并通过点周围四个点的像素值判断是否为铁路和道路，
"""


class DiffVillageOrRoad:

    def __init__(self, contours, image_thread, around_distance, point_distance, area_value):
        """
        :param contours: 铁路和大车路轮廓
        :param image_rgb: 图片路径
        :param distance: 周围像素距离中心点的距离
        """
        self.contours = contours
        assert isinstance(self.contours, list)
        self.image_thread = image_thread
        self.white_rgb_value = 255
        self.distance = around_distance
        self.point_distance = point_distance
        self.area_value = area_value

    def get_diff_vilageroad_or_road_point(self):
        """
        实现轮廓的提取,输入为提取后的轮廓列表-输出为两点组成线段的集合或返回点的集合
        :return:
        """
        railway_point_list = []
        road_point_list = []

        for single_line_point in self.contours:
            # 转换为列表
            location_list = single_line_point.tolist()
            lenght = len(location_list)

            # 长度为1：点，长度为2：线段，长度为3或以上：拐点
            for single_lenght in range(lenght):
                point_location_list = location_list[single_lenght][0]

                # 坐标点越界丢弃不判断
                road_types = self.get_around_road_point(self.image_thread, point_location_list, self.distance, self.area_value)
                if road_types is None:
                    continue

                # 0表示为铁路， 1表示为国省道
                if road_types is False:
                    railway_point_list.append(point_location_list)
                else:
                    road_point_list.append(point_location_list)

        # 列表转换为轮廓点(线段)
        railway_point_contours, road_point_contours = Utils.list_to_contours_line(self.point_distance,
                                                                                  railway_point_list, road_point_list)
        # railway_point_contours, road_point_contours = Utils.list_to_contours_point(railway_point_list, road_point_list)
        return railway_point_contours, road_point_contours

    def get_diff_vilageroad_or_road_line(self):
        """
        实现轮廓的提取,提取后与原来集合中的子轮廓相同，保持不变
        :return:
        """
        railway_point_contours = []
        road_point_contours = []
        tmp_railway_list = []
        tmp_road_list = []

        for single_line_point in self.contours:
            # 转换为列表
            location_list = single_line_point.tolist()
            lenght = len(location_list)

            tmp_railway_list.copy()
            tmp_railway_list.clear()
            tmp_road_list.copy()
            tmp_road_list.clear()

            single_railway_number = 0
            single_road_number = 0

            # 长度为1：点，长度为2：线段，长度为3或以上：拐点
            for single_lenght in range(lenght):
                point_location_list = location_list[single_lenght][0]

                # 坐标点越界丢弃不判断
                road_types = self.get_around_road_point(self.image_thread, point_location_list, self.distance, self.area_value)
                if road_types is None:
                    continue

                if road_types is False:
                    single_railway_number += 1
                    tmp_railway_list.append([point_location_list])
                else:
                    single_road_number += 1
                    tmp_road_list.append([point_location_list])

            if len(tmp_railway_list) > math.floor(lenght/2):
                railway_point_contours.append(single_line_point)
            else:
                road_point_contours.append(single_line_point)
        return railway_point_contours, road_point_contours

    def get_around_road_point(self, image_thread, point: list, distance, arae_value):
        """
        选择像素点的值
        :param image_thread:rgb格式的图像
        :param point:点阵
        :param distance:中心点距离
        :return: 0:铁路， 1：国省道
        """
        if len(point) != 2 or not isinstance(distance, int):
            print("point location is not list or int")
            return

        def read_image_rgb(point_location, white_rgb_value):
            rgb = image_thread[point_location[1], point_location[0]]
            if rgb == white_rgb_value:
                return True
            return False

        area_point_number = 0
        height, width = image_thread.shape
        try:
            # 上下四个点的坐标
            center_x_location, center_y_location = point[0], point[1]

            # 防止越界
            max_x_point, max_y_point = min(center_x_location + distance, width - 1), min(center_y_location + distance-1, height - 1)
            min_x_point, min_y_point = max(center_x_location - distance, 0), max(center_y_location - distance, 0)

            for y in range(min_y_point, max_y_point + 1):
                for x in range(min_x_point, max_x_point + 1):
                    # 搜索白色区域
                    if read_image_rgb((x, y), self.white_rgb_value):
                        area_point_number += 1
            area_rectangle = round(area_point_number / math.exp(2), 2)

            # 白色区域超过一定范围判断该点为大车路
            if area_rectangle > arae_value:
                return True
            else:
                return False
        except Exception as e:
            print(e)


class Railway:
    def __init__(self, arange_value, image):
        self.arange_value = arange_value
        self.image = image

    def conculate_distance(self, point1, point2):
        return np.sqrt(np.sum((point1 - point2) ** 2))

    def search_arange_point(self, center_point, width, height):
        x, y = center_point[0], center_point[1]
        min_x, max_x = max(0, x - self.arange_value), min(x + self.arange_value, width)
        min_y, max_y = max(0, y - self.arange_value), min(y + self.arange_value, height)

        x_range = [i for i in range(min_x, max_x)]
        y_range = [i for i in range(min_y, max_y)]
        return x_range, y_range

    def point_connect_line(self, point_contours: list):
        """
        实现点之间的相互连接
        :return:
        """
        if not isinstance(point_contours, list):
            print("railway reconezed is wrong!")
            return
        if len(point_contours) == 0:
            return point_contours

        center_point_union = []
        final_point_union = []
        part_point_union = []
        for index, single_contours in enumerate(point_contours):

            # 计算中心点,向上取整
            center_point = single_contours[int(len(single_contours) / 2)]
            center_point_union.append(center_point)

        height, width = self.image.shape
        tmp_list = []
        for index in range(len(center_point_union)):
            single_center_point = center_point_union[0]

            # 从列表第一个点开始遍历
            x_range, y_range = self.search_arange_point(single_center_point, width, height)
            pop_point = center_point_union.pop(0)
            if index == 0:
                part_point_union.append(pop_point)

            # 对搜索在范围内的剩余点
            tmp_list.clear()
            for point in center_point_union:
                x_location, y_location = point[0], point[1]
                if x_location in x_range and y_location in y_range:
                    tmp_list.append(point)

            # 搜索的范围内不存在点，默认此时铁路已经中断，单独为一段铁路
            if len(tmp_list) == 0:
                if len(part_point_union) == 0:
                    continue

                final_point_union.append(part_point_union)
                part_point_union = part_point_union.copy()
                part_point_union.clear()
                continue

            # 对范围内的多个点进行距离测算
            tmp_value = 1000
            tmp_map = {}
            for tmp in tmp_list:
                tmp_distance = self.conculate_distance(np.array(tmp), np.array(single_center_point))
                if tmp_distance in tmp_map.keys():
                    continue
                tmp_map[tmp_distance] = tmp
                tmp_value = min(tmp_distance, tmp_value)

            point = tmp_map[tmp_value]
            part_point_union.append(point)
        return final_point_union











































